package websocket

import (
	"fmt"
	"net/http"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	"github.com/zeromicro/go-zero/core/logx"
)

type Conn struct {
	*websocket.Conn
	Uid               string
	s                 *Server
	mu                sync.Mutex
	idle              time.Time
	maxConnectionIdle time.Duration
	done              chan struct{}

	msgMu           sync.Mutex
	readMessages    []*Message
	readMessagesSeq map[string]*Message
	messageCh       chan *Message
}

func NewConn(s *Server, w http.ResponseWriter, r *http.Request) *Conn {
	c, err := s.upgrader.Upgrade(w, r, nil)
	if err != nil {
		s.Errorf("upgrade http to websocket err:%v", err)
		return nil
	}

	conn := &Conn{
		Conn:              c,
		s:                 s,
		idle:              time.Now(),
		done:              make(chan struct{}),
		maxConnectionIdle: s.opt.maxConnectionIdle,
		readMessages:      make([]*Message, 0, 2),
		readMessagesSeq:   make(map[string]*Message),

		messageCh: make(chan *Message, 1), //只给一个容量,保证消息顺序且一定程度缓解阻塞
	}
	go conn.keepalive()
	return conn
}

func (c *Conn) keepalive() {
	idleTime := time.NewTimer(c.maxConnectionIdle)
	defer idleTime.Stop()

	for {
		select {
		case <-idleTime.C:
			c.mu.Lock()
			idle := c.idle
			fmt.Printf("idle %v,maxidle:%v \n", c.idle, c.maxConnectionIdle)

			if idle.IsZero() {

				c.mu.Unlock()
				idleTime.Reset(c.maxConnectionIdle)
				continue
			}
			val := c.maxConnectionIdle - time.Since(idle)
			fmt.Printf("val :%v \n", val)
			c.mu.Unlock()
			if val <= 0 {
				c.Close()
				return
			}
			idleTime.Reset(val)
		case <-c.done:
			fmt.Println("客户端已关闭")
			return
		}
	}
}

func (c *Conn) appendMsgMq(msg *Message) {
	c.msgMu.Lock()
	defer c.msgMu.Unlock()

	if m, ok := c.readMessagesSeq[msg.Id]; ok {
		if len(c.readMessages) == 0 {
			logx.Info("消息已被丢弃，请重发原始消息")
			return
		}
		if m.Id != msg.Id || m.AckSeq >= msg.AckSeq {
			return
		}

		c.readMessagesSeq[msg.Id] = msg
		return
	}

	if msg.FrameType == FrameAck {
		//
		logx.Info("原始消息已被丢弃,此ACK确认无效")
		return
	}

	c.readMessagesSeq[msg.Id] = msg
	c.readMessages = append(c.readMessages, msg)
}

func (c *Conn) ReadMessage() (messageType int, p []byte, err error) {
	messageType, p, err = c.Conn.ReadMessage()
	c.mu.Lock()
	defer c.mu.Unlock()
	c.idle = time.Time{}
	return
}

func (c *Conn) WriteMessage(messageType int, data []byte) error {
	c.mu.Lock()
	err := c.Conn.WriteMessage(messageType, data)
	c.idle = time.Now()
	c.mu.Unlock()
	return err
}

func (c *Conn) Close() error {
	select {
	case <-c.done:
	default:
		close(c.done)
	}

	return c.Conn.Close()
}
